//
//  MKUIFlowLayoutConstraint.m
//  MKUI
//
//  Created by sushangjin on 15/4/14.
//  Copyright (c) 2015年 sushangjin. All rights reserved.
//

#import "MKUIFlowLayoutConstraint.h"
#import "MKUILayoutConstraintItemAttribute.h"

@interface MKUIFlowLayoutConstraint()
@property(nonatomic,strong) MKUILayoutConstraintItemAttributeSection *itemAttributeSection;
@end

@implementation MKUIFlowLayoutConstraint
- (MKCGAxis)layoutDirectionAxis{
    MKCGAxis X = self.layoutDirection==MKUILayoutConstraintDirectionHorizontal?MKCGAxisX:MKCGAxisY;
    return X;
}
- (CGSize)itemSizeForItem:(id<MKUILayoutConstraintItemProtocol>)item thatFits:(CGSize)size resizeItems:(BOOL)resizeItems{
    CGSize itemSize = CGSizeZero;
    CGSize limitSize = size;
    if(resizeItems){
        if([item respondsToSelector:@selector(sizeThatFits:resizeItems:)]){
            itemSize = [item sizeThatFits:limitSize resizeItems:resizeItems];
        }else if([item respondsToSelector:@selector(sizeThatFits:)]){
            itemSize = [item sizeThatFits:limitSize];
        }else{
            itemSize = [item sizeOfLayout];
        }
    }else{
        itemSize = [item sizeOfLayout];
    }
    return itemSize;
}
- (nullable MKUILayoutConstraintItemAttributeSection *)itemAttributeSectionThatFits:(CGSize)size resizeItems:(BOOL)resizeItems needLimitSize:(BOOL)needLimitSize{
    NSArray *items = self.layoutedItems;
    NSInteger count = items.count;
    if(count==0) return nil;
    BOOL needRevert = (self.layoutDirection==MKUILayoutConstraintDirectionHorizontal&&self.layoutHorizontalAlignment==MKUILayoutConstraintHorizontalAlignmentRight)
    ||(self.layoutDirection==MKUILayoutConstraintDirectionVertical&&self.layoutVerticalAlignment==MKUILayoutConstraintVerticalAlignmentBottom)
    ;
    if(needRevert){
        NSMutableArray *tmp = [[NSMutableArray alloc] init];
        for (id<MKUILayoutConstraintItemProtocol> item in items.reverseObjectEnumerator){
            [tmp addObject:item];
        }
        items = tmp;
    }
    
    MKCGAxis X = [self layoutDirectionAxis];
    MKCGAxis Y = MKCGAxisReverse(X);
    
    UIEdgeInsets contentInsets = self.contentInsets;
    
    CGFloat xSpacing = self.interitemSpacing;
    
    CGSize originLimitSize = CGSizeMake(size.width-contentInsets.left-contentInsets.right, size.height-contentInsets.top-contentInsets.bottom);//限制在 size -contentInsets 矩形内
    
    CGSize limitSize = originLimitSize;
    limitSize.width = MAX(0,limitSize.width);
    limitSize.height = MAX(0,limitSize.height);
    //以下类命名，以水平流动时为基准
    
    MKUILayoutConstraintItemAttributeSection *line = [[MKUILayoutConstraintItemAttributeSection alloc] init];
    for(int i=0;i<items.count;i++){
        id<MKUILayoutConstraintItemProtocol> item = items[i];
        CGFloat limitWidth = MKCGSizeGetLength(limitSize, X);
//        CGFloat limitHeight = MKCGSizeGetLength(limitSize, Y);
        CGSize itemSize = [self itemSizeForItem:item thatFits:limitSize resizeItems:resizeItems];
        MKUILayoutConstraintItemAttribute *itemAttr = [[MKUILayoutConstraintItemAttribute alloc] initWithItem:item];
        [line addItemAttribute:itemAttr];
        CGFloat w = MKCGSizeGetLength(itemSize, X);
        CGFloat h = MKCGSizeGetLength(itemSize, Y);
        if(needLimitSize){
            w = MIN(w,MKCGSizeGetLength(limitSize, X));
            h = MIN(h,MKCGSizeGetLength(limitSize, Y));
            MKCGSizeSetLength(&itemSize, X, w);
            MKCGSizeSetLength(&itemSize, Y, h);
        }
        itemAttr.size = itemSize;
        if(w>0 && h>0){
            limitWidth -= xSpacing+w;
            limitWidth = MAX(0,limitWidth);
            MKCGSizeSetLength(&limitSize, X, limitWidth);
        }
    }
    if(needRevert){
        NSMutableArray *tmp = [[NSMutableArray alloc] init];
        for (id<MKUILayoutConstraintItemProtocol> item in line.itemAttributs.reverseObjectEnumerator){
            [tmp addObject:item];
        }
        line.itemAttributs = tmp;
    }
    
    line.size = [line sizeThatFlowLayoutItemsWithSpacing:xSpacing axis:X];
    return line;
}

- (CGSize)sizeThatFits:(CGSize)size resizeItems:(BOOL)resizeItems{
    CGSize sizeFits = CGSizeZero;
    UIEdgeInsets contentInsets = self.contentInsets;
    MKUILayoutConstraintItemAttributeSection *line = [self itemAttributeSectionThatFits:size resizeItems:resizeItems needLimitSize:NO];
    sizeFits = line.layoutFrame.size;
    if(sizeFits.width>0&&sizeFits.height>0){
        sizeFits.width += contentInsets.left+contentInsets.right;
        sizeFits.height += contentInsets.top+contentInsets.bottom;
    }
    return sizeFits;
}
- (void)layoutItems{
    [self layoutItemsWithResizeItems:NO];
}
- (void)layoutItemsWithResizeItems:(BOOL)resizeItems{
    CGSize size = self.bounds.size;
    
    MKUILayoutConstraintItemAttributeSection *line = [self itemAttributeSectionThatFits:size resizeItems:resizeItems needLimitSize:!self.unLimitItemSizeInBounds];
    MKCGAxis X = [self layoutDirectionAxis];
    MKCGAxis Y = MKCGAxisReverse(X);
    
    UIEdgeInsets contentInsets = self.contentInsets;
    
    CGFloat xSpacing = self.interitemSpacing;
    
    MKCGRectAlignment alignX = self.layoutDirection==MKUILayoutConstraintDirectionVertical?MKCGRectAlignmentFromMKUILayoutConstraintVerticalAlignment(self.layoutVerticalAlignment):MKCGRectAlignmentFromMKUILayoutConstraintHorizontalAlignment(self.layoutHorizontalAlignment);
    MKCGRectAlignment alignY = self.layoutDirection==MKUILayoutConstraintDirectionHorizontal?MKCGRectAlignmentFromMKUILayoutConstraintVerticalAlignment(self.layoutVerticalAlignment):MKCGRectAlignmentFromMKUILayoutConstraintHorizontalAlignment(self.layoutHorizontalAlignment);
    
    CGRect bounds = UIEdgeInsetsInsetRect(self.bounds, contentInsets);
    CGRect f1 = line.layoutFrame;
    MKCGRectAlignToRect(&f1, Y, alignY, bounds);
    MKCGRectAlignToRect(&f1, X, alignX, bounds);
    line.layoutFrame = f1;
    
    [line flowLayoutItemsWithSpacing:xSpacing axis:X alignment:alignY needRevert:NO];
    for(MKUILayoutConstraintItemAttribute *item in line.itemAttributs){
        [item applyAttributeWithResizeItems:resizeItems];
    }
    self.itemAttributeSection = line;
}

- (id)copyWithZone:(NSZone *)zone{
    MKUIFlowLayoutConstraint *obj = [super copyWithZone:zone];
    obj.layoutDirection = self.layoutDirection;
    obj.layoutVerticalAlignment = self.layoutVerticalAlignment;
    obj.layoutHorizontalAlignment = self.layoutHorizontalAlignment;
    obj.contentInsets = self.contentInsets;
    obj.interitemSpacing = self.interitemSpacing;
    obj.unLimitItemSizeInBounds = self.unLimitItemSizeInBounds;
    return obj;
}
- (BOOL)isEmptyBounds:(CGRect)bounds withResizeItems:(BOOL)resizeItems{
	BOOL is = NO;
	NSArray *items = self.layoutedItems;
	if(items.count){
		CGRect b = UIEdgeInsetsInsetRect(bounds, self.contentInsets);
		
		CGFloat interitemSpacing = self.interitemSpacing;
		CGSize limitSize = b.size;
		MKCGAxis axis = self.layoutDirection==MKUILayoutConstraintDirectionHorizontal?MKCGAxisX:MKCGAxisY;
		is = YES;
		for (id<MKUILayoutConstraintItemProtocol> item in items){
			CGRect f1 = b;
			CGSize f1_size;
			if(resizeItems){
				if([item respondsToSelector:@selector(sizeThatFits:resizeItems:)]){
					f1_size = [item sizeThatFits:limitSize resizeItems:resizeItems];
				}else if([item respondsToSelector:@selector(sizeThatFits:)]){
					f1_size = [item sizeThatFits:limitSize];
				}else{
					f1_size = [item sizeOfLayout];
				}
			}else{
				if([item respondsToSelector:@selector(sizeThatFits:)]){
					f1_size = [item sizeThatFits:limitSize];
				}else if([item respondsToSelector:@selector(sizeThatFits:resizeItems:)]){
					f1_size = [item sizeThatFits:limitSize resizeItems:resizeItems];
				}else{
					f1_size = [item sizeOfLayout];
				}
			}
			if(!CGSizeEqualToSize(f1_size, CGSizeZero)){
				is = NO;
				break;
			}
			f1.size.width = MIN(f1.size.width,f1_size.width);
			f1.size.height = MIN(f1.size.height,f1_size.height);
			MKCGRectSetLength(&f1, axis, MIN(MKCGSizeGetLength(limitSize, axis),MKCGRectGetLength(f1, axis)));
			MKCGSizeSetLength(&limitSize, axis, MKCGSizeGetLength(limitSize, axis)-(MKCGRectGetLength(f1, axis)+interitemSpacing));
			MKCGSizeSetLength(&limitSize, axis, MAX(0, MKCGSizeGetLength(limitSize, axis)));
		}
	}else{
		is = YES;
	}
    return is;
}
@end

MKDEF_EnumTypeCategories(MKUIFlowLayoutConstraintParam,
(@{
   @(MKUIFlowLayoutConstraintParam_H_C_C):@"H_C_C",
   @(MKUIFlowLayoutConstraintParam_H_C_L):@"H_C_L",
   @(MKUIFlowLayoutConstraintParam_H_C_R):@"H_C_R",
   @(MKUIFlowLayoutConstraintParam_H_T_C):@"H_T_C",
   @(MKUIFlowLayoutConstraintParam_H_T_L):@"H_T_L",
   @(MKUIFlowLayoutConstraintParam_H_T_R):@"H_T_R",
   @(MKUIFlowLayoutConstraintParam_H_B_L):@"H_B_L",
   @(MKUIFlowLayoutConstraintParam_H_B_C):@"H_B_C",
   @(MKUIFlowLayoutConstraintParam_H_B_R):@"H_B_R",
   @(MKUIFlowLayoutConstraintParam_V_C_C):@"V_C_C",
   @(MKUIFlowLayoutConstraintParam_V_C_L):@"V_C_L",
   @(MKUIFlowLayoutConstraintParam_V_C_R):@"V_C_R",
   @(MKUIFlowLayoutConstraintParam_V_T_C):@"V_T_C",
   @(MKUIFlowLayoutConstraintParam_V_T_L):@"V_T_L",
   @(MKUIFlowLayoutConstraintParam_V_T_R):@"V_T_R",
   @(MKUIFlowLayoutConstraintParam_V_B_C):@"V_B_C",
   @(MKUIFlowLayoutConstraintParam_V_B_L):@"V_B_L",
   @(MKUIFlowLayoutConstraintParam_V_B_R):@"V_B_R",
   }))
@implementation MKUIFlowLayoutConstraint(InitMethod)
//////////////////////////////////////////////////////////////////////////////
+ (NSDictionary<NSNumber *,NSArray<NSNumber *> *> *)ConstraintParamMap{
	static NSDictionary<NSNumber *,NSArray<NSNumber *> *> * __share__;
	static dispatch_once_t onceToken;
	dispatch_once(&onceToken, ^{
		NSMutableDictionary<NSNumber *,NSArray<NSNumber *> *> *map = [[NSMutableDictionary alloc] init];
		map[@(MKUIFlowLayoutConstraintParam_H_C_C)] = @[
		  @(MKUILayoutConstraintDirectionHorizontal),
		  @(MKUILayoutConstraintVerticalAlignmentCenter),
		  @(MKUILayoutConstraintHorizontalAlignmentCenter),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_H_C_L)] = @[
		  @(MKUILayoutConstraintDirectionHorizontal),
		  @(MKUILayoutConstraintVerticalAlignmentCenter),
		  @(MKUILayoutConstraintHorizontalAlignmentLeft),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_H_C_R)] = @[
		  @(MKUILayoutConstraintDirectionHorizontal),
		  @(MKUILayoutConstraintVerticalAlignmentCenter),
		  @(MKUILayoutConstraintHorizontalAlignmentRight),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_H_T_C)] = @[
		  @(MKUILayoutConstraintDirectionHorizontal),
		  @(MKUILayoutConstraintVerticalAlignmentTop),
		  @(MKUILayoutConstraintHorizontalAlignmentCenter),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_H_T_L)] = @[
		  @(MKUILayoutConstraintDirectionHorizontal),
		  @(MKUILayoutConstraintVerticalAlignmentTop),
		  @(MKUILayoutConstraintHorizontalAlignmentLeft),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_H_T_R)] = @[
		  @(MKUILayoutConstraintDirectionHorizontal),
		  @(MKUILayoutConstraintVerticalAlignmentTop),
		  @(MKUILayoutConstraintHorizontalAlignmentRight),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_H_B_L)] = @[
		  @(MKUILayoutConstraintDirectionHorizontal),
		  @(MKUILayoutConstraintVerticalAlignmentBottom),
		  @(MKUILayoutConstraintHorizontalAlignmentLeft),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_H_B_C)] = @[
		  @(MKUILayoutConstraintDirectionHorizontal),
		  @(MKUILayoutConstraintVerticalAlignmentBottom),
		  @(MKUILayoutConstraintHorizontalAlignmentCenter),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_H_B_R)] = @[
		  @(MKUILayoutConstraintDirectionHorizontal),
		  @(MKUILayoutConstraintVerticalAlignmentBottom),
		  @(MKUILayoutConstraintHorizontalAlignmentRight),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_V_C_C)] = @[
		  @(MKUILayoutConstraintDirectionVertical),
		  @(MKUILayoutConstraintVerticalAlignmentCenter),
		  @(MKUILayoutConstraintHorizontalAlignmentCenter),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_V_C_L)] = @[
		  @(MKUILayoutConstraintDirectionVertical),
		  @(MKUILayoutConstraintVerticalAlignmentCenter),
		  @(MKUILayoutConstraintHorizontalAlignmentLeft),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_V_C_R)] = @[
		  @(MKUILayoutConstraintDirectionVertical),
		  @(MKUILayoutConstraintVerticalAlignmentCenter),
		  @(MKUILayoutConstraintHorizontalAlignmentRight),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_V_T_C)] = @[
		  @(MKUILayoutConstraintDirectionVertical),
		  @(MKUILayoutConstraintVerticalAlignmentTop),
		  @(MKUILayoutConstraintHorizontalAlignmentCenter),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_V_T_L)] = @[
		  @(MKUILayoutConstraintDirectionVertical),
		  @(MKUILayoutConstraintVerticalAlignmentTop),
		  @(MKUILayoutConstraintHorizontalAlignmentLeft),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_V_T_R)] = @[
		  @(MKUILayoutConstraintDirectionVertical),
		  @(MKUILayoutConstraintVerticalAlignmentTop),
		  @(MKUILayoutConstraintHorizontalAlignmentRight),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_V_B_C)] = @[
		  @(MKUILayoutConstraintDirectionVertical),
		  @(MKUILayoutConstraintVerticalAlignmentBottom),
		  @(MKUILayoutConstraintHorizontalAlignmentCenter),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_V_B_L)] = @[
		  @(MKUILayoutConstraintDirectionVertical),
		  @(MKUILayoutConstraintVerticalAlignmentBottom),
		  @(MKUILayoutConstraintHorizontalAlignmentLeft),
		  ];
		map[@(MKUIFlowLayoutConstraintParam_V_B_R)] = @[
		  @(MKUILayoutConstraintDirectionVertical),
		  @(MKUILayoutConstraintVerticalAlignmentBottom),
		  @(MKUILayoutConstraintHorizontalAlignmentRight),
		  ];
		__share__ = map;
	});
	return __share__;
}
+ (NSDictionary<NSArray<NSNumber *> *,NSNumber *> *)ConstraintParamRevertMap{
	static NSDictionary<NSArray<NSNumber *> *,NSNumber *> * __share__;
	static dispatch_once_t onceToken;
	dispatch_once(&onceToken, ^{
		NSDictionary<NSNumber *,NSArray<NSNumber *> *> *ConstraintParamMap = [self ConstraintParamMap];
		NSMutableDictionary<NSArray<NSNumber *> *,NSNumber *> *map = [[NSMutableDictionary alloc] initWithCapacity:ConstraintParamMap.count];
		for (NSNumber *key in ConstraintParamMap) {
			map[ConstraintParamMap[key]] = key;
		}
		__share__ = map;
	});
	return __share__;
}
+ (void)parseConstraintParam:(MKUIFlowLayoutConstraintParam)param layoutDirection:(MKUILayoutConstraintDirection *)layoutDirection layoutVerticalAlignment:(MKUILayoutConstraintVerticalAlignment *)layoutVerticalAlignment layoutHorizontalAlignment:(MKUILayoutConstraintHorizontalAlignment *)layoutHorizontalAlignment{
	NSDictionary<NSNumber *,NSArray<NSNumber *> *> *ConstraintParamMap = [self.class ConstraintParamMap];
	NSArray<NSNumber *> *enums = ConstraintParamMap[@(param)];
	*layoutDirection = [enums[0] integerValue];
	*layoutVerticalAlignment = [enums[1] integerValue];
	*layoutHorizontalAlignment = [enums[2] integerValue];
}
+ (MKUIFlowLayoutConstraintParam)constraintParamWithLayoutDirection:(MKUILayoutConstraintDirection)layoutDirection layoutVerticalAlignment:(MKUILayoutConstraintVerticalAlignment)layoutVerticalAlignment layoutHorizontalAlignment:(MKUILayoutConstraintHorizontalAlignment)layoutHorizontalAlignment{
	NSDictionary<NSArray<NSNumber *> *,NSNumber *> *ConstraintParamRevertMap = [self.class ConstraintParamRevertMap];
	MKUIFlowLayoutConstraintParam param = (MKUIFlowLayoutConstraintParam)[ConstraintParamRevertMap[@[@(layoutDirection),@(layoutVerticalAlignment),@(layoutHorizontalAlignment)]] integerValue];
	return param;
}
//////////////////////////////////////////////////////////////////////////////
- (id)initWithItems:(NSArray<id<MKUILayoutConstraintItemProtocol>> *)items constraintParam:(MKUIFlowLayoutConstraintParam)param contentInsets:(UIEdgeInsets)contentInsets interitemSpacing:(CGFloat)interitemSpacing{
	if(self=[self init]){
		self.items = items;
		self.contentInsets = contentInsets;
		self.interitemSpacing = interitemSpacing;
		[self configWithConstraintParam:param];
	}
	return self;
}
- (void)setConstraintParam:(MKUIFlowLayoutConstraintParam)constraintParam{
	[self configWithConstraintParam:constraintParam];
}
- (MKUIFlowLayoutConstraintParam)constraintParam{
	MKUIFlowLayoutConstraintParam param = [self.class constraintParamWithLayoutDirection:self.layoutDirection layoutVerticalAlignment:self.layoutVerticalAlignment layoutHorizontalAlignment:self.layoutHorizontalAlignment];
	return param;
}
- (void)configWithConstraintParam:(MKUIFlowLayoutConstraintParam)param{
	MKUILayoutConstraintDirection layoutDirection;
	MKUILayoutConstraintVerticalAlignment layoutVerticalAlignment;
	MKUILayoutConstraintHorizontalAlignment layoutHorizontalAlignment;
	[self.class parseConstraintParam:param layoutDirection:&layoutDirection layoutVerticalAlignment:&layoutVerticalAlignment layoutHorizontalAlignment:&layoutHorizontalAlignment];
	self.layoutDirection = layoutDirection;
	self.layoutVerticalAlignment = layoutVerticalAlignment;
	self.layoutHorizontalAlignment = layoutHorizontalAlignment;
}
@end
